﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DafYomiPortalLib
{
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Xml.Linq;
    using DafYomiLib;

    public class MediaMetadata
    {
        private readonly XDocument m_lessonsMetadataXmlDoc;

        private readonly Dictionary<Tractate, List<Rabbi>> m_tractatesToRabbisMap;

        private readonly HebrewComparer m_hebrewComparer = new HebrewComparer();

        private MediaMetadata(string mediaMetadataXml)
        {
            m_lessonsMetadataXmlDoc = XDocument.Parse(mediaMetadataXml);
            var root = m_lessonsMetadataXmlDoc.Root;
            var version = root.Attribute("version").Value;
            var tractates = root.Elements("lessons");
            m_tractatesToRabbisMap = tractates.ToDictionary(
                tractate => (Tractate)Enum.Parse(typeof(Tractate), tractate.Elements().First().Attribute("masechtId").Value),
                tractate =>
                tractate.Elements()
                        .Select(
                            a =>
                            new Rabbi(a.Attribute("magidShiorId").Value, a.Attribute("magidShior").Value, a.Attribute("language").Value)).ToList());
        }

        public async static Task<MediaMetadata> DownloadMediaMetadataAsync()
        {
            var request = (HttpWebRequest)WebRequest.Create(MediaPaths.LessonsMetadataUrl);

            // We have to turn off ReadStreamBuffering, as it will try to download the whole stream before we can do anything, which is BAD!
            request.AllowReadStreamBuffering = false;

            var response = (HttpWebResponse) await request.GetResponseAsync();
            if (response != null)
            {
                using (var reader = new StreamReader(response.GetResponseStream()))
                {
                    string responseString = await reader.ReadToEndAsync();
                    return new MediaMetadata(responseString);
                }
            }

            return null;
        }

        public IEnumerable<Rabbi> GetRabbisGivingLessonsFor(Tractate tractate)
        {
            if (m_tractatesToRabbisMap == null)
            {
                return null;
            }

            return m_tractatesToRabbisMap[tractate];
        }

        public IEnumerable<Rabbi> AllRabbis()
        {
            if (m_tractatesToRabbisMap == null)
            {
                return null;
            }

            return m_tractatesToRabbisMap.SelectMany(a => a.Value).Distinct().OrderBy(a => a.Name, m_hebrewComparer);
        }

        public IEnumerable<Tractate> AllTractatesRabbiGivesLessons(Rabbi rabbi)
        {
            if (m_tractatesToRabbisMap == null)
            {
                return null;
            }
            
            return m_tractatesToRabbisMap.Where(b => b.Value.Contains(rabbi)).Select(a => a.Key);
        }

        public IEnumerable<Tractate> AllTractatesRabbiGivesLessons(IEnumerable<Rabbi> rabbis)
        {
            if (m_tractatesToRabbisMap == null)
            {
                return null;
            }

            return m_tractatesToRabbisMap.Where(b => b.Value.Intersect(rabbis).Any()).Select(a => a.Key);
        }

        private class HebrewComparer : IComparer<string>
        {
            public int Compare(string x, string y)
            {
                for (int i = 0; i < Math.Min(x.Length, y.Length); i++)
                {
                    int result;
                    if (IsHebrewCharacter(x[i]) && IsHebrewCharacter(y[i]))
                    {
                        result = CompareCharacters(x[i], y[i]);
                        if (result == 0)
                        {
                            continue;
                        }

                        return result;
                    }

                    if (IsHebrewCharacter(x[i]))
                    {
                        return -1;
                    }

                    if (IsHebrewCharacter(y[i]))
                    {
                        return 1;
                    }

                    result = CompareCharacters(x[i], y[i]);
                    if (result != 0)
                    {
                        return result;
                    }
                }

                if (x.Length < y.Length)
                {
                    return -1;
                }

                if (x.Length > y.Length)
                {
                    return 1;
                }

                // x.Length == y.Length
                return 0;
            }

            private bool IsHebrewCharacter(char c)
            {
                return c >= 'א' && c <= 'ת';
            }

            private int CompareCharacters(char c1, char c2)
            {
                if (c1 > c2)
                {
                    return 1;
                }

                if (c1 < c2)
                {
                    return -1;
                }

                return 0;
            }
        }
    }
}
